package com.rent.auth.service.impl;

import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.rent.auth.config.AuthPropertiesConfig;
import com.rent.auth.mapper.AdminMapper;
import com.rent.auth.model.Admin;
import com.rent.auth.model.Company;
import com.rent.auth.model.Role;
import com.rent.auth.model.RolePermission;
import com.rent.auth.pojo.AdminUserDetails;
import com.rent.auth.pojo.AuthToken;
import com.rent.auth.pojo.LoginForm;
import com.rent.auth.service.IAdminService;
import com.rent.auth.service.ICompanyService;
import com.rent.auth.service.IRolePermissionService;
import com.rent.auth.service.IRoleService;
import com.rent.common.exception.Asserts;
import com.rent.common.service.RedisService;
import com.rent.common.utils.ContextUtil;
import com.rent.common.utils.DateUtil;
import com.rent.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Service;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;

import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zn
 * @since 2025-01-17
 */
@Slf4j
@Service
public class AdminServiceImpl extends MyBaseService<AdminMapper, Admin, Integer> implements IAdminService {

    @Autowired
    private IRoleService roleService;

    @Autowired
    private ICompanyService companyService;

    @Autowired
    private IRolePermissionService rolePermissionService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthPropertiesConfig config;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisService redisService;

    //令牌持久化存储接口
    @Autowired
    private TokenStore tokenStore;


    @Override
    public AdminUserDetails loadUserByUsernameWithTenant(String username, String tenant) {
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(tenant)) return null;
        if (!NumberUtil.isInteger(tenant)) return null;
        LambdaQueryWrapper<Admin> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(Admin::getId);
        wrapper.eq(Admin::getUsername, username);
        List<Admin> adminList = getBaseMapper().selectList(wrapper);
        if (CollectionUtils.isEmpty(adminList)) return null;
        Admin admin = adminList.get(0);
        if (admin.getCompanyId() == null) return null;
        // 判断组织状态
        Company company = companyService.selectByPrimaryKey(admin.getCompanyId());
        if (company == null) return null;
        if (company.getStatus() == null || company.getStatus() == 0) Asserts.fail("组织未启用");
        if (company.getIndate() != null && !DateUtil.compareToDate(company.getIndate())) Asserts.fail("组织已过期");
        if (admin.getIsMax() == null || admin.getIsMax() == 1) {
            return new AdminUserDetails(admin, null);
        }
        Integer cid = Integer.parseInt(tenant);
        if (!cid.equals(admin.getCompanyId())) Asserts.fail("组织代码传入错误");
        if (admin.getRole() == null) Asserts.fail("用户未分配角色");
        Role role = new Role();
        role.setStatus(1);
        role.setId(admin.getRole());
        List<Role> roleList = roleService.selectListBySearch(role);
        if (CollectionUtils.isEmpty(roleList)) Asserts.fail("用户未分配角色");
        List<RolePermission> permissionList = rolePermissionService.selectListByRoles(roleList);
        if (CollectionUtils.isEmpty(permissionList)) Asserts.fail("用户未分配资源");
        return new AdminUserDetails(admin, permissionList);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public AuthToken authLogin(LoginForm loginForm) {
        if (loginForm == null) return null;
        String username = StringUtils.trim(loginForm.getUsername());
        if (StringUtils.isEmpty(username)) return null;
        String password = StringUtils.trim(loginForm.getPassword());
        if (StringUtils.isEmpty(password)) return null;
        String tenant = StringUtils.trim(loginForm.getTenantCode());
        if (StringUtils.isEmpty(tenant)) return null;
        AdminUserDetails details = loadUserByUsernameWithTenant(username, tenant);
        if (details == null) return null;
        Admin admin = details.getAdmin();
        if (!passwordEncoder.matches(password, admin.getPassword())) Asserts.fail("用户名或密码错误");
        // 获取访问链接
        ServiceInstance choose = loadBalancerClient.choose("security");
        String url = NET_PROTOCOL + config.getDomain() + ":" + config.getPort() + config.getSecurityUrl();
        if (choose != null) {
            url = choose.getUri().toString() + config.getSecurityUrl();
        }
        log.info("获取认证安全访问链接" + url);
        //2.定义头信息 (有client id 和client secr)
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add(AUTHORIZE_TOKEN, httpBasic(config.getClientId(), config.getClientSecret()));
        //3. 定义请求体  有授权模式 用户的名称 和密码
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.add("grant_type", config.getGrandType());
        formData.add("username", username);
        formData.add("password", password);
        formData.add("tenant", tenant);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(formData, headers);
        ResponseEntity<Map> response = null;
        try {
            response = restTemplate.exchange(url, HttpMethod.POST, request, Map.class);
        } catch (Exception e) {
            log.info("请求授权异常:{}", e.getMessage());
        }
        if (response == null) Asserts.fail("安全认证失败");
        Map map = response.getBody();
        if (CollectionUtils.isEmpty(map)) return null;
        /* 成功后设置线程数据 */
        // 设置租户
        ContextUtil.setTenant(tenant);
        // 设置用户名
        ContextUtil.setUsername(username);
        // 登录信息
        AuthToken authToken = new AuthToken();
        //访问令牌(jwt)
        String accessToken = (String) map.get("access_token");
        //刷新令牌(jwt)
        String refreshToken = (String) map.get("refresh_token");
        //jti，作为用户的身份标识
        String jwtToken = (String) map.get("jti");
        // 令牌过期时间
        String expires = String.valueOf(map.get("expires_in"));
        // 设置信息
        authToken.setJti(jwtToken);
        authToken.setAccessToken(TOKEN_HEADER + accessToken);
        authToken.setRefreshToken(refreshToken);
        authToken.setExpires(Long.parseLong(expires));
        authToken.setHeader("Authorization");
        authToken.setDescription("登录成功");
        return authToken;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public AuthToken authRefreshToken(String refreshToken) {
        String token = StringUtils.trim(refreshToken);
        if (StringUtils.isEmpty(token)) return null;
        // 获取访问链接
        ServiceInstance choose = loadBalancerClient.choose("security");
        String url = NET_PROTOCOL + config.getDomain() + ":" + config.getPort() + config.getSecurityUrl();
        if (choose != null) {
            url = choose.getUri().toString() + config.getSecurityUrl();
        }
        log.info("获取认证安全访问链接" + url);
        //2.定义头信息 (有client id 和client secr)
        MultiValueMap<String, String> requestHeaders = new LinkedMultiValueMap<>();
        requestHeaders.add(AUTHORIZE_TOKEN, httpBasic(config.getClientId(), config.getClientSecret()));
        //3. 定义请求体  有授权模式 用户的名称 和密码
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.add("grant_type", GRAND_TYPE_REFRESH);
        formData.add("refresh_token", token);
        HttpEntity<MultiValueMap<String, String>> requestData = new HttpEntity<>(formData, requestHeaders);
        ResponseEntity<Map> response = null;
        try {
            response = restTemplate.exchange(url, HttpMethod.POST, requestData, Map.class);
        } catch (Exception e) {
            log.info("请求授权异常:{}", e.getMessage());
        }
        if (response == null) Asserts.fail("安全认证失败");
        Map map = response.getBody();
        if (CollectionUtils.isEmpty(map)) return null;
        // 登录信息
        AuthToken authToken = new AuthToken();
        //访问令牌(jwt)
        String accessToken = (String) map.get("access_token");
        //刷新令牌(jwt)
        String refreshJwt = (String) map.get("refresh_token");
        //jti，作为用户的身份标识
        String jwtToken = (String) map.get("jti");
        // 令牌过期时间
        String expires = String.valueOf(map.get("expires_in"));
        // 设置信息
        authToken.setJti(jwtToken);
        authToken.setAccessToken(TOKEN_HEADER + accessToken);
        authToken.setRefreshToken(refreshJwt);
        authToken.setExpires(Long.parseLong(expires));
        authToken.setHeader("Authorization");
        authToken.setDescription("刷新成功");
        return authToken;
    }

    @Override
    public void logout() {
        String token = getAuthorizeToken();
        if (StringUtils.isEmpty(token)) return;
        log.info("退出登录token:{}", token);
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
        log.info("获取到的token信息:{}", oAuth2AccessToken);
        if (oAuth2AccessToken != null) {
            OAuth2Authentication authentication = tokenStore.readAuthentication(oAuth2AccessToken);
            //获取token唯一标识
//            String jti = (String) oAuth2AccessToken.getAdditionalInformation().get("jti");
            if (authentication != null) {
                log.info("退出用户为:{}", authentication.getName());
                // 把token 加入至缓存中 如果存在则代表 此token 已过期
                redisService.set(oAuth2AccessToken.getValue(), authentication.getName(), oAuth2AccessToken.getExpiresIn());
            }
        }
    }

}
